/**
 * Copyright (c) 2023 murenchao
 * taomu is licensed under Mulan PubL v2.
 * You can use this software according to the terms and conditions of the Mulan PubL v2.
 * You may obtain a copy of Mulan PubL v2 at:
 *       http://license.coscl.org.cn/MulanPubL-2.0
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PubL v2 for more details.
 */
package cool.taomu.box.spi

import com.google.inject.Guice
import cool.taomu.box.guice.TaomuGuiceModule
import java.util.Collection
import java.util.ServiceLoader
import java.util.concurrent.ConcurrentSkipListMap
import org.slf4j.LoggerFactory

interface ITaomuServiceLoader<E> {
	def E get(String name);

	def E get(Class<E> zlass);

	def E first();

	def Collection<E> getAll();
}

class TaomuServiceLoader<E> implements ITaomuServiceLoader<E> {
	val static LOG = LoggerFactory.getLogger(TaomuServiceLoader);
	val ConcurrentSkipListMap<String, E> providers = new ConcurrentSkipListMap();
	Class<E> zlass;
	ClassLoader loader;
	var tgmodule = new TaomuGuiceModule();

	new(Class<E> zlass) {
		this(zlass, Thread.currentThread.contextClassLoader);
	}

	new(Class<E> zlass, ClassLoader loader) {
		this.zlass = zlass;
		this.loader = loader;
	}
	
	private synchronized def init() {
		if (providers.isEmpty) {
			var ServiceLoader<E> serviceLoader = ServiceLoader.load(zlass, loader);
			LOG.info("spi size:{}",serviceLoader.size);
			serviceLoader.iterator.forEach [ s |
				var module = tgmodule.build(s.class)
				var name = s.class.getAnnotation(Alias) !== null ? s.class.getAnnotation(Alias).value : s.class.name;
				LOG.info("加载 {}", s.class.name);
				if (module !== null) {
					LOG.info("解析Box");
					val tg = Guice.createInjector(module);
					providers.put(name, tg.getInstance(s.class) as E);
				} else {
					providers.put(name, s);
				}
			]
		}
	}

	override get(String name) {
		init();
		return providers.get(name);
	}

	override getAll() {
		init();
		return providers.values;
	}

	override first() {
		init();
		return providers.values.get(0);
	}

	override get(Class<E> zlass) {
		return this.get(zlass.name);
	}

}
